#include "tsina.h"
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#include "ufs.h"
using namespace std;
using namespace Facebook;


#define MAX_CONTENT_SIZE  280
//#define  MAX_FILE_SIZE  1048576
#define  MAX_FILE_SIZE  4194304


//bool Facebook::Api::debugMode = false;
string Facebook::Api::errorCode;
string Facebook::Api::errorInfo;
//const string Facebook::Api::DefaultAppKey = "4262714414";// wu yao's
//const string Facebook::Api::DefaultAppKey = "1194754959";// Company's
//const string Facebook::Api::DefaultAppKey = "3436390207";// m's






namespace
{

     void getValue(const StringMap& m, const string& name, string& value)
     {
          StringMap::const_iterator f = m.find(name);
          if (f == m.end())
               value.clear();
          else
               value = f->second;
     }


     string getValue(const StringMap& m, const string& name)
     {
          StringMap::const_iterator f = m.find(name);
          if (f == m.end())
               return string();

          return f->second;
     }
     
     void bindParas(string& url, const StringMap& m)
     {
          if( m.empty() ) return;

          
          url += "?"; 
          for(StringMap::const_iterator it = m.begin(); it != m.end(); ++it)
               if (!it->second.empty())
                    url = url + it->first + "=" + it->second + "&";
          
          string::iterator pos = url.end();
          pos --;
          url.erase(pos);
     }

}


int Facebook::toInt(const string& str)
{
     try
     {
          return  lexical_cast<int>(str);
     }
     catch(...)
     { }
     return 0;
}


Facebook::User::User()
     //:status(boost::shared_ptr<Status>(new Status()))
{
     followers_count = 0;
     friends_count = 0;
     statuses_count = 0;
     favourites_count = 0;
}

Facebook::User::~User()
{
}

Facebook::User::User(const StringMap& m)
{
     set(m);	
}


void Facebook::User::set(const StringMap& m)
{
     getValue(m, "id", id);
     getValue(m, "screen_name", screen_name);
     getValue(m, "province", province);
     getValue(m, "city", city);
     getValue(m, "location", location);
     getValue(m, "description", description);
     getValue(m, "url", url);
     getValue(m, "profile_image_url", profile_image_url);
     getValue(m, "domain", domain);
     getValue(m, "gender", gender);
     getValue(m, "created_at", created_at);
     getValue(m, "verified", verified);
     followers_count =  toInt(getValue(m, "followers_count"));
     friends_count =  toInt(getValue(m, "friends_count"));
     statuses_count =  toInt(getValue(m,"statuses_count"));
     favourites_count=  toInt(getValue(m,"favourites_count"));
}


Facebook::Status::Status()
{
}

Facebook::Status::~Status()
{
}


Facebook::Status::Status(const StringMap& sm, const StringMap& um)
{
     setProperties(sm);
     setUser(um);
}

void Facebook::Status::set(const StringMap& sm, const StringMap& um)
{
     setProperties(sm);
     setUser(um);
}


void Facebook::Status::setProperties(const StringMap& sm)
{
     getValue(sm, "created_at", created_at);
     getValue(sm, "id", id);
     getValue(sm, "text", text);
     getValue(sm, "thumbnail_pic", thumbnail_pic);
     getValue(sm, "bmiddle_pic", bmiddle_pic);
     getValue(sm, "original_pic", original_pic);
     getValue(sm, "in_reply_to_screen_name", in_reply_to_screen_name);
     getValue(sm, "in_reply_to_status_id", in_reply_to_status_id);
     getValue(sm, "in_reply_to_user_id", in_reply_to_user_id);
     getValue(sm, "source", source);
     getValue(sm, "truncated", truncated);
     getValue(sm, "favorited", favorited);
}

void Facebook::Status::setUser(const StringMap& um)
{
     user.set(um);
}

void Facebook::Status::setRT(const StringMap& rtsm, const StringMap& rtum)
{
     if( !rtsm.empty())
     {
          retweeted_status = boost::shared_ptr<Status>(new Status(rtsm, rtum));
     }
}

Facebook::Comment::Comment()
{
}

Facebook::Comment::~Comment()
{
}

void	Facebook::Comment::setProperties(const StringMap& cm)
{
     getValue(cm, "id", id);
     getValue(cm, "text", text);
     getValue(cm, "favorited", favorited);
     getValue(cm, "truncated", truncated);
     getValue(cm, "created_at", created_at);
}


void	Facebook::Comment::setReply(const StringMap& sm)
{
}

Facebook::Api::Api(const string& format, const int retry_count, 
               const int retry_delay, const int sleep_dalay)
     ://username(user_name), password(pass_word), appkey(app_key), 
      format(format), retry_delay(retry_delay),
      retry_count(retry_count), sleep_delay(sleep_delay)
{
     urlprefix = "http://api.t.sina.com.cn/";
     //m_curl = shared_ptr<Curltools>(new Curltools(user_name, pass_word,app_key));
     m_curl = shared_ptr<Curltools>(new Curltools);
}

void Facebook::Api::oAuthLogin(const string& key, const string& secret, const string& filename)
{
     m_curl -> oAuthLogin(key, secret, filename);
}


Facebook::Api::~Api()
{
}


bool Facebook::Api::getPublicTimeline(Statuses& ss)
{
     ss.clear();
     
     string apiurl = urlprefix + "statuses/public_timeline." + format;
     StringMap paras;
     paras["source"] = appkey;
     bindParas(apiurl, paras);
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          int err_info = 	checkError(apiurl, buffer) ;
          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
			
               BasicXMLParser parser;
               parser.parseXml2Statuses(buffer,ss);
               return true;
          }
     }
     return false;
}
/*
  get statues of current user's friends
*/
bool	Facebook::Api::getFriendsTimeline(Statuses& ss, 
                                      const string& since_id, 
                                      const string& max_id, 
                                      const int page,
                                      const int count,
                                      const string& feature)
{

     ss.clear();
          
     string apiurl = urlprefix + "statuses/friends_timeline." + format;

     StringMap  paras;
     paras["source"] = appkey;
     if (!since_id.empty()) paras["since_id"] = since_id;
     if (!max_id.empty())paras["max_id"] = max_id;
     
     paras["count"] = Ufs::toString(count);
     paras["page"] = Ufs::toString(page);
     if (!feature.empty()) paras["feature"] = feature;
     

     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          //cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parseXml2Statuses(buffer,ss);
               return true;
          }
     }
     return false;

}




bool	Facebook::Api::getRepostTimeline(Statuses& ss,
                                     const string& status_id, 
                                     const string& since_id, 
                                     const string& max_id, 
                                     const int page,
                                     const int count)
{

     ss.clear();
          
     string apiurl = urlprefix + "statuses/repost_timeline." + format;

     StringMap  paras;
     paras["source"] = appkey;
     paras["id"] = status_id;
     paras["count"] = Ufs::toString(count);
     paras["since_id"] = since_id;
     paras["max_id"] = max_id;
     paras["page"] = Ufs::toString(page);

     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parseXml2Statuses(buffer,ss);
               return true;
          }
     }
     return false;

}



bool	Facebook::Api::getUserTimeline(Statuses& ss,
                                   const string& user_id,
                                   const string& screen_name, 
                                   const string& since_id,
                                   const string& max_id, 
                                   const int page,
                                   const int count)
{
     string apiurl = urlprefix + "statuses/user_timeline." + format;
     StringMap paras;
     paras["source"] = appkey;

     if (user_id != "")
          paras["user_id"] = user_id;
     else if(screen_name != "")
          paras["screen_name"] = screen_name;
     else 
     {
          cout << "err request at "<< apiurl << endl;
          return false;
     }
     paras["max_id"] = max_id;
     paras["since_id"] = since_id;
     paras["count"] = Ufs::toString(count);
     paras["page"] = Ufs::toString(page);

     bindParas(apiurl, paras);

     //	cout << apiurl << endl;
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl -> getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser paraser;
               paraser.parseXml2Statuses(buffer,ss);
               return true;
          }
     }
     return false;
}
/*
  get the statuses @me
*/
bool	Facebook::Api::getMentionsAtMe(Statuses& ss, 
                                   const string& since_id, 
                                   const string& max_id, 
                                   const int page,
                                   const int count) 
{
     ss.clear();
          
     string apiurl = urlprefix + "statuses/mentions." + format;

     StringMap  paras;
     paras["source"] = appkey;
     paras["count"] = Ufs::toString(count);
     paras["since_id"] = since_id;
     paras["max_id"] = max_id;
     paras["page"] = Ufs::toString(page);

     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser paraser;
               paraser.parseXml2Statuses(buffer,ss);
               return true;
          }
     }
     return false;
}
/*
  get comments of a status
*/
bool	Facebook::Api::getComments(Comments& cs,
                               const string& status_id,
                               const int page,
                               const int count)
{
     cs.clear();
     
     string apiurl = urlprefix + "statuses/comments." + format;

     StringMap  paras;
     paras["source"] = appkey;
     paras["id"] = status_id;
     paras["count"] = Ufs::toString(count);
     paras["page"] = Ufs::toString(page);

     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parseXml2Comments(buffer,cs);
               return true;
          }
     }
     return false;


}

/*
  get counts of RTs and comments of statuses
  counts["id"].first = rt_counts;
  counts["id"].second = comment_counts;
*/
bool	Facebook::Api::getRTCounts(map<string, pair<int,int> >& counts,
                               const vector<string>& status_ids)
{
     string apiurl = urlprefix + "statuses/counts." + format;

     string ids = (status_ids.empty()) ? "" : status_ids[0] ;
     for(int i = 1; i < status_ids.size(); ++i)
          ids += "," + status_ids[i];
     
     StringMap  paras;
     paras["source"] = appkey;
     paras["ids"] = ids;

     bindParas(apiurl, paras);
     
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               TiXmlElement* root;
               parser.parse(buffer);
               parser.getRootNode(root);
               vector<StringMap > mvec;
               parser.node2maplist(root, "count", mvec);

               for(int i = 0; i < mvec.size(); ++i)
               {
                    string id;
                    getValue(mvec[i], "id", id);
                    int comment_counts = toInt(getValue(mvec[i],"comments"));
                    int rt_counts = toInt(getValue(mvec[i],"rt"));;	
                    counts.insert(make_pair(id, make_pair(rt_counts, comment_counts)));
               }
               return true;
          }
     }
     return false;
}

bool	Facebook::Api::showUser(User& u, 
                            const string& user_id, 
                            const string& screen_name)
{
     string apiurl = urlprefix + "users/show." + format;
     StringMap paras;
     paras["source"] = appkey;

     if (user_id != "")
          paras["user_id"] = user_id;
     else if(screen_name != "")
          paras["screen_name"] = screen_name;
     else 
     {
          cout << "err request at "<< apiurl << endl;
          return false;
     }

     bindParas(apiurl, paras);

     //	cout << apiurl << endl;
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl -> getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               if (!parser.parse(buffer)) return false;
               
               TiXmlElement* root;
               parser.getRootNode(root);
               parser.parseNode2User(root, u);
               return true;
          }
     }
     return false;

}

bool	Facebook::Api::deleteStatus(const string& status_id)
{
     string apiurl = urlprefix + "statuses/destroy/" + status_id + "." + format;
     StringMap paras;
     paras["source"] = appkey;

     bindParas(apiurl, paras);

     //	cout << apiurl << endl;
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl -> postDelete(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               return true;
          }
     }
     return false;


}

bool	Facebook::Api::showStatus(Status& s, const string& status_id)
{
     string apiurl = urlprefix + "statuses/show/" + status_id + "." + format;
     StringMap paras;
     paras["source"] = appkey;

     bindParas(apiurl, paras);

     //	cout << apiurl << endl;
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl -> getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parseXml2Status(buffer, s);
               return true;
          }
     }
     return false;


}

bool	Facebook::Api::RTStatus(const string& status_id, const string& content0)
{
     // if( content.length() > MAX_CONTENT_SIZE )
     //     content = content.substr(0, MAX_CONTENT_SIZE);

     string apiurl = urlprefix + "statuses/repost." + format;
     string contentprefix = "source=" + appkey + "&id=" + status_id;

     string content = content0;
     if( content == "")
          content = contentprefix;
     else 
          content = contentprefix + "&status=" + Ufs::urlEncode(content) ; 
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl->postContent(apiurl, content);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               //		m_xmlparser -> parseXml2Status(buffer, s);
               return true;
          }
     }
     return false;	
}


/*
  update status
*/
bool Facebook::Api::updateStatus(const string& content0, const string& filename)
{
     // todo: check the content size
     string content = content0;
     
     if( filename.empty() )
     {
          string apiurl = urlprefix + "statuses/update." + format;
          string contentprefix = "source=" + appkey + "&status=";
          content = contentprefix + Facebook::urlEncode(content) ; 
          int cnt = 0;
          while(cnt++ < retry_count)
          {
               string buffer = m_curl->postContent(apiurl, content);
               int err_info = 	checkError(apiurl, buffer) ;

               if(err_info == 1) 
               {
                    if (!waitOnHttpError()) return false;
                    continue;
               }
               else if(err_info < 0)
               {
                    return false;
               }
               else
               {
                    //		m_xmlparser -> parseXml2Status(buffer, s);
                    return true;
               }
          }
     }
     else 
     {
          if( !Ufs::exists(filename) )
          {
               cout << "the file <" << filename << "> does not exist.\n";
               return false;
          }
          if( Ufs::getFileSize(filename) > MAX_FILE_SIZE )
          {
               cout << "the file <" << filename << "> is too large!\n";
               return false;
          }
          string apiurl = urlprefix + "statuses/upload." + format;
          content = Facebook::urlEncode(content) ;  // do not encode while post?
          int cnt = 0;
          while(cnt++ < retry_count)
          {
               string buffer = m_curl->postFile(apiurl, content, filename);
               int err_info = 	checkError(apiurl, buffer) ;

               if(err_info == 1) 
               {
                    if (!waitOnHttpError()) return false;
                    continue;
               }
               else if(err_info < 0)
               {
                    return false;
               }
               else
               {
                    //		m_xmlparser -> parseXml2Status(buffer, s);
                    return true;
               }
          }

     }
     return false;	
}


/*
  check whether from_userid is following to_userid 
*/
bool	Facebook::Api::followship(BoolPair& followship, const string& to_userid, const string& from_userid)
{
     string apiurl = urlprefix + "friendships/show." + format;
     StringMap paras; 
     paras["source"] = appkey;
     paras["target_id"] = to_userid;
     paras["source_id"] = from_userid;
     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl->getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               // continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parse(buffer);
               TiXmlElement* node;
               parser.getRootNode(node);
               node = node -> FirstChildElement("source");
               StringMap m;
               parser.node2map(node, m);
               if( m["following"] == "true" )
                    followship.first = true;
               else followship.first = false;

               if( m["followed_by"] == "true")
                    followship.second = true;
               else followship.second = false;

               return true;
          }
     }
     return false;
}


int Facebook::Api::numIgnoredErrors = 0;
bool Facebook::Api::follow(const string& user_id, const string& screen_name)
{
     string apiurl = urlprefix + "friendships/create." + format;	
     StringMap paras;

     if (user_id != "")
          paras["user_id"] = user_id;
     else if(screen_name != "")
          paras["screen_name"] = screen_name;
     else 
     {
          cout << "err request at "<< apiurl << endl;
          return false;
     }
     paras["source"] = appkey;
     bindParas(apiurl, paras);
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string content;
          string buffer = m_curl->postContent(apiurl, content);
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               if (contains(errorInfo, "500"))
               {
                    cout << "api error: " << errorInfo << "\n";
                    return false;
               }
               
               //  if (errorInfo == "40028:null") return true;
               if (contains(errorInfo, "null"))
               {
                    //if (debugMode)
                    ++Facebook::Api::numIgnoredErrors;
                    cout << "api error ignored: " << errorInfo << "\n";
                    return true;
               }
                  
               return false;
          }
          else
          {
               // m_xmlparser -> parseXml2Status(buffer, s);
               return true;
          }
     }
     
     return false;

}


bool	Facebook::Api::unfollow(const string& user_id, const string& screen_name)
{
     string apiurl = urlprefix + "friendships/destroy." + format;	
     StringMap paras;
     if (user_id != "")
          paras["user_id"] = user_id;
     else if(screen_name != "")
          paras["screen_name"] = screen_name;
     else 
     {
          cout << "err request at "<< apiurl << endl;
          return false;
     }
     paras["source"] = appkey;
     bindParas(apiurl, paras);
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string content;
          string buffer = m_curl->postContent(apiurl, content);
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               if (contains(errorInfo, "500"))
               {
                    cout << "api error: " << errorInfo << "\n";
                    return false;
               }
               
               if (contains(errorInfo, "null"))
               {
                    ++Facebook::Api::numIgnoredErrors;
                    cout << "api error ignored: " << errorInfo << "\n";
                    return true;
               }
               return false;
          }
          else
          {
               // m_xmlparser -> parseXml2Status(buffer, s);
               return true;
          }
     }
     return false;

}

bool Facebook::Api::searchUsers(Users& us, string& query, bool sort_by_time, const int page,
                            const int count)
{
     string apiurl = urlprefix + "users/search." + format;
     StringMap paras;
     paras["source"] = appkey;
     paras["q"] = Ufs::urlEncode(query);
     paras["page"] = Ufs::toString(page);
     paras["count"] = Ufs::toString(count);
     if(sort_by_time)
          paras["sort"] = "1";
     else paras["sort"] = "2";

     bindParas(apiurl, paras);

     //	cout << apiurl << endl;
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl -> getURL(apiurl);
          //cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parseXml2Users(buffer, us);
               return true;
          }
     }
     return false;
}

bool Facebook::Api::searchStatuses(Statuses& ss, string& query, bool sort_by_time, const int page,
                               const string& user_id, const string& starttime, const string& endtime, const int count)
{
     string apiurl = urlprefix + "statuses/search." + format;
     StringMap paras;
     paras["source"] = appkey;
     paras["q"] = Ufs::urlEncode(query);
     paras["starttime"] = starttime;
     paras["endtime"] = endtime;
     paras["page"] = Ufs::toString(page);
     paras["count"] = Ufs::toString(count);
     paras["fuid"] = user_id;
     if(sort_by_time)
          paras["sort"] = "1";
     else paras["sort"] = "2";

     bindParas(apiurl, paras);

     //	cout << apiurl << endl;
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl -> getURL(apiurl);
          cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parseXml2Statuses(buffer, ss);
               return true;
          }
     }
     return false;
}

/*
  get the current statuses of one's friends
*/
bool	Facebook::Api::getFriends(Users& us, 
                              const string& user_id, 
                              const string& screen_name, 
                              const string& cursor, 
                              const int count)
{	
     string next_cursor(cursor);
     while( next_cursor != "0")
     {
          string apiurl = urlprefix + "statuses/friends." + format;
          StringMap  paras;
          if (user_id != "")
               paras["user_id"] = user_id;
          else if(screen_name != "")
               paras["screen_name"] = screen_name;
          else 
          {
               cout << "err request at "<< apiurl << endl;
               return false;
          }
          paras["source"] = appkey;
          paras["count"] = Ufs::toString(count);
          paras["cursor"] = next_cursor;
          bindParas(apiurl, paras);

          int cnt = 0;
          while(cnt++ < retry_count)
          {
               string buffer = m_curl ->getURL(apiurl);
               //		cout << buffer << endl;
               int err_info = 	checkError(apiurl, buffer) ;
               if(err_info == 1) 
               {
                    if (!waitOnHttpError()) return false;
                    continue;
               }
               else if(err_info < 0)
               {
                    return false;
               }
               else
               {
                    BasicXMLParser parser;
                    if (!parser.parse(buffer))
                         return false;
                    
                    TiXmlElement*  root;
                    TiXmlElement* node;
                    parser.getRootNode(root);
                    for(TiXmlElement* item = root->FirstChildElement("user"); item; 
						item = item->NextSiblingElement("user"))

                    {
                         User u;
                         parser.parseNode2User(item, u);
                         us.push_back(u);
                    }
                    node = root -> FirstChildElement("next_cursor");
                    next_cursor = (node -> GetText())? node->GetText() : "0" ;
                    break;
               }
          }
     }
     return true;
}

/*	
  get the current statuses of one's followers
*/
bool	Facebook::Api::getFollowers(Users& us, 
                                const string& user_id,
                                const string& screen_name, 
                                const string& cursor, 
                                const int count,
                                const int total)
{

     //cout << "------------\n";
     string next_cursor(cursor);
     while( next_cursor != "0" && us.size() < total)
     {
          //UFS_DEBUG(us.size());
          string apiurl = urlprefix + "statuses/followers." + format;
          StringMap  paras;
          if (user_id != "")
               paras["user_id"] = user_id;
          else if(screen_name != "")
               paras["screen_name"] = screen_name;
          else 
          {
               cout << "err request at "<< apiurl << endl;
               return false;
          }
          paras["source"] = appkey;
          paras["count"] = Ufs::toString(count);
          paras["cursor"] = next_cursor;
          bindParas(apiurl, paras);

          int cnt = 0;
          while(cnt++ < retry_count)
          {
               string buffer = m_curl ->getURL(apiurl);
               //		cout << buffer << endl;
               
               int err_info = 	checkError(apiurl, buffer) ;
               if(err_info == 1) 
               {
                    if (!waitOnHttpError()) return false;
                    continue;
               }
               else if(err_info < 0)
               {
                    return false;
               }
               else
               {
                    BasicXMLParser parser;
                    if (!parser.parse(buffer))
                         return false;
                    
                    TiXmlElement* root;
                    TiXmlElement* node;
                    parser.getRootNode(root);
                    for(TiXmlElement* item = root->FirstChildElement("user"); item; 
						item = item->NextSiblingElement("user"))

                    {
                         User u;
                         parser.parseNode2User(item, u);
                         us.push_back(u);
                    }
                    node = root -> FirstChildElement("next_cursor");
                    next_cursor = (node -> GetText())? node->GetText() : "0" ;
                    break;
               }
          }
     }
     return true;

}


bool Facebook::Api::waitOnHttpError()
{
     cout << "wait on http empty error...\n";
     int reset_time;
     //  if (rateLimitStatus(reset_time) <= 0) return false;
     sleep(2); //   old version:   sleep(reset_time);
     return true;
}

bool Facebook::Api::getFriendIds(vector<string>& idlist, 
                             const string& user_id,
                             const string& screen_name, 
                             const string& cursor, 
                             const int count )
{
     idlist.clear();
     string next_cursor(cursor);
     while( next_cursor != "0")
     {
          string apiurl = urlprefix + "friends/ids." + format;
          StringMap  paras;
          if (user_id != "")
               paras["user_id"] = user_id;
          else if(screen_name != "")
               paras["screen_name"] = screen_name;
          else 
          {
               cout << "err request at "<< apiurl << endl;
               return false;
          }
          paras["source"] = appkey;
          paras["count"] = Ufs::toString(count);
          paras["cursor"] = next_cursor;
          bindParas(apiurl, paras);

          int cnt = 0;
          while(cnt++ < retry_count)
          {
               string buffer = m_curl ->getURL(apiurl);
               //cout << buffer << endl;
               
               int err_info = 	checkError(apiurl, buffer) ;
               if(err_info == 1) 
               {
                    if (!waitOnHttpError()) return false;
                    continue;
               }
               else if(err_info < 0)
               {
                    return false;
               }
               else
               {
                    BasicXMLParser parser;
                    parser.parse(buffer);
                    TiXmlElement* root;
                    TiXmlElement* node;
                    parser.getRootNode(root);
                    node = root -> FirstChildElement("ids");
                    for(TiXmlElement* item = node ->FirstChildElement("id"); item; 
						item = item->NextSiblingElement("id"))

                    {
                         if( item -> GetText() != NULL )
                         {
                              string id = item->GetText();
                              idlist.push_back(id);
                         }
                    }
                    node = root -> FirstChildElement("next_cursor");
                    next_cursor = (node -> GetText())? node->GetText() : "0" ;
                    break;
               }
          }
     }
     return true;


}


bool	Facebook::Api::getFollowerIds(vector<string>& idlist, 
                                  const string& user_id,
                                  const string& screen_name, 
                                  const string& cursor, 
                                  const int count)
{
     // int count = 500;
     string next_cursor(cursor);
     while( next_cursor != "0")
     {
          string apiurl = urlprefix + "followers/ids." + format;
          StringMap  paras;
          if (user_id != "")
               paras["user_id"] = user_id;
          else if(screen_name != "")
               paras["screen_name"] = screen_name;
          else 
          {
               cout << "err request at "<< apiurl << endl;
               return false;
          }
          paras["source"] = appkey;
          paras["count"] = Ufs::toString(count);
          paras["cursor"] = next_cursor;
          bindParas(apiurl, paras);

          int cnt = 0;
          while(cnt++ < retry_count)
          {
               //UFS_DEBUG(apiurl);
               string buffer = m_curl ->getURL(apiurl);
               
               //if (buffer.empty()) return false;
               
               //		cout << buffer << endl;
               int err_info = 	checkError(apiurl, buffer) ;
               if(err_info == 1) 
               {
                    if (!waitOnHttpError()) return false;
                    continue;

               }
               else if(err_info < 0)
               {
                    return false;
               }
               else
               {
                    // if (next_cursor != "-1")
                    // cout << buffer;
                    BasicXMLParser parser;
                    if (!parser.parse(buffer)) return false;
                    TiXmlElement* root;
                    TiXmlElement* node;
                    parser.getRootNode(root);
                    node = root -> FirstChildElement("ids");
                    for(TiXmlElement* item = node ->FirstChildElement("id"); item; 
						item = item->NextSiblingElement("id"))

                    {
                         if( item -> GetText() != NULL )
                         {
                              string id = item->GetText();
                              idlist.push_back(id);
                         }
                    }
                    node = root -> FirstChildElement("next_cursor");
                    next_cursor = (node -> GetText())? node->GetText() : "0" ;
                    break;
               }
          }
     }
     return true;
}

int		Facebook::Api::rateLimitStatus(int& reset_time)
{
     string apiurl = urlprefix + "account/rate_limit_status." + format;

     StringMap  paras;
     paras["source"] = appkey;
     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          // cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;
          if(err_info !=  0) 
          {
               continue;
          }
          else
          {
               //cout << buffer << endl;
               BasicXMLParser parser;
               parser.parse(buffer);
               TiXmlElement* root;
               parser.getRootNode(root);
               StringMap m;
               parser.node2map(root, m);
               int remain_cnt = toInt(getValue(m, "remaining-hits"));
               reset_time = toInt(getValue(m,"reset-time-in-seconds"));
               return remain_cnt;
          }
     }
     
     cout << "erro in check_error \n";
     return 0;
}

bool	Facebook::Api::logout()
{

     string apiurl = urlprefix + "account/end_session." + format;

     StringMap  paras;
     paras["source"] = appkey;
     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string content = "";
          string buffer = m_curl ->postContent(apiurl, content);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;
          if(err_info !=  0) 
          {
               continue;
          }
          else
          {
               return true;
          }
     }
     cout << "error in logout \n";
     return false;

}

int Facebook::Api::checkError(const string& url, string& page)
{
     errorCode.clear();
     errorInfo.clear();

     trim(page);

     // maybe retry
     if (page.empty() ||
         (starts_with(page, "<?") && ends_with(page, "?>"))
          )
     {
          if (icontains(url, "account/verify_credentials.")
              || icontains(url, "friends/ids.")
              || icontains(url, "followers/ids.")
               )
               return 1; // retry

          
          if (icontains(url, "friendships/create.")
              || icontains(url, "friendships/destroy.")
               )
          {
               errorInfo = "null";
          }
          
          return -2;
     }

     
     if( page.find_first_of("<?xml") == string::npos )
     {
          cout << url <<" is not a XML file" << endl;
          return -2; // fatal error
     }

     
     const boost::regex error_code_ex("<error_code>([0-9]+)</error_code>");
     const boost::regex error_info_ex("<error>(.*)</error>");
     boost::match_results<string::const_iterator> what;
     string::const_iterator start = page.begin(), end = page.end();
     
     if(boost::regex_search(start, end, what, error_code_ex))
     {
          errorCode = string(what[1].first, what[1].second);
          trim(errorCode);

          if(boost::regex_search(start, end, what, error_info_ex))
          {
               errorInfo = string(what[1].first, what[1].second);
               trim(errorInfo);
          }                    
                         
          //if (debugMode)
          {
               UFS_DEBUG(gbk(page));
               cout << "Request ERROR :\n		ERROR_CODE: " << errorCode 
                    << "\n		ERROR_INFO: " << errorInfo 
                    << "\n		Request url: " << url << endl;
          }
          
          return -1; // fatal error
     }
     
     return 0;
}


bool Facebook::Api::verifyAccount(User& u)
{
     // http://api.t.sina.com.cn/account/verify_credentials.xml?source=appkey
     string apiurl = urlprefix + "account/verify_credentials." + format;
     StringMap paras;
     paras["source"] = appkey;
     
     bindParas(apiurl, paras);

     //	cout << apiurl << endl;
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl -> getURL(apiurl);
          // cout << buffer << endl;
               
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parse(buffer);
               TiXmlElement* root;
               parser.getRootNode(root);
               parser.parseNode2User(root, u);
               return true;
          }
     }
     return false;
}





bool	Facebook::Api::commentStatus(const string& status_id, const string& content0)
{
     string apiurl = urlprefix + "statuses/comment." + format;
     string content = "source=" + appkey + "&id=" + status_id + "&comment=" + Ufs::urlEncode(content0) ;
     
     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl->postContent(apiurl, content);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               //if (!waitOnHttpError()) return false;
               //continue;
               return false;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               // m_xmlparser -> parseXml2Comment(buffer, s);
               return true;
          }
     }
     return false;	
}


bool	Facebook::Api::getCommentsTimeline(Comments& ss, 
                                       const string& since_id, 
                                       const string& max_id, 
                                       const int page,
                                       const int count)
{

     ss.clear();
          
     string apiurl = urlprefix + "statuses/comments_timeline." + format;

     StringMap  paras;
     paras["source"] = appkey;
     paras["count"] = Ufs::toString(count);
     paras["since_id"] = since_id;
     paras["max_id"] = max_id;
     paras["page"] = Ufs::toString(page);

     bindParas(apiurl, paras);

     int cnt = 0;
     while(cnt++ < retry_count)
     {
          string buffer = m_curl ->getURL(apiurl);
          //		cout << buffer << endl;
          int err_info = 	checkError(apiurl, buffer) ;

          if(err_info == 1) 
          {
               if (!waitOnHttpError()) return false;
               continue;
          }
          else if(err_info < 0)
          {
               return false;
          }
          else
          {
               BasicXMLParser parser;
               parser.parseXml2Comments(buffer,ss);
               return true;
          }
     }
     return false;

}



